1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.hiprenderer.backend.gl.glrenderer; 12 13 version(Android) 14 { 15 public import gles.gl30; 16 } 17 else version(PSVita) 18 { 19 public import gles; 20 } 21 else version(WebAssembly) 22 { 23 public import gles; 24 } 25 else version(Have_bindbc_opengl) 26 { 27 public import bindbc.opengl; 28 } 29 version(OpenGL): 30 31 import hip.hiprenderer.config; 32 import hip.hiprenderer.renderer; 33 import hip.hiprenderer.framebuffer; 34 import hip.hiprenderer.shader; 35 import hip.hiprenderer.viewport; 36 import hip.windowing.window; 37 import hip.util.conv; 38 import hip.math.rect; 39 import hip.console.log; 40 import hip.error.handler; 41 42 import hip.hiprenderer.backend.gl.gltexture; 43 import hip.hiprenderer.backend.gl.glframebuffer; 44 import hip.hiprenderer.backend.gl.glshader; 45 46 private __gshared bool errorCheckEnabled = true; 47 48 auto glCall(T)(scope T delegate() dg, string file = __FILE__, size_t line = __LINE__) 49 { 50 import hip.config.opts; 51 static if(is(T == void)) 52 dg(); 53 else 54 auto ret = dg(); 55 version(WebAssembly) 56 { 57 static if(HIP_DEBUG_WEBGL) 58 { 59 if(errorCheckEnabled) 60 HipRenderer.exitOnError(file, line); 61 } 62 } 63 else static if(HIP_DEBUG_GL) 64 { 65 if(errorCheckEnabled) 66 HipRenderer.exitOnError(file, line); 67 } 68 static if(!is(T == void)) 69 return ret; 70 } 71 72 GLenum fromHipStencilFunc(HipStencilTestingFunction fn) 73 { 74 final switch(fn) with(HipStencilTestingFunction) 75 { 76 case Never: return GL_NEVER; 77 case Always: return GL_ALWAYS; 78 case Less: return GL_LESS; 79 case LessEqual: return GL_LEQUAL; 80 case Greater: return GL_GREATER; 81 case GreaterEqual: return GL_GEQUAL; 82 case Equal: return GL_EQUAL; 83 case NotEqual: return GL_NOTEQUAL; 84 } 85 } 86 87 GLenum fromHipStencilOperation(HipStencilOperation op) 88 { 89 final switch(op) with(HipStencilOperation) 90 { 91 case Keep: return GL_KEEP; 92 case Zero: return GL_ZERO; 93 case Replace: return GL_REPLACE; 94 case Increment: return GL_INCR; 95 case IncrementWrap: return GL_INCR_WRAP; 96 case Decrement: return GL_DECR; 97 case DecrementWrap: return GL_DECR_WRAP; 98 case Invert: return GL_INVERT; 99 } 100 } 101 102 /** 103 * 104 * Those functions here present are fairly inneficient as there is not batch ocurring, 105 * as I don't understand how to implement it right now, I'll mantain those functions for having 106 * static access to drawing 107 */ 108 class Hip_GL3Renderer : IHipRendererImpl 109 { 110 HipWindow window; 111 Shader currentShader; 112 protected __gshared bool isGLBlendEnabled = false; 113 protected __gshared bool isGLDepthEnabled = false; 114 protected __gshared bool isGLStencilEnabled = false; 115 protected __gshared GLenum mode; 116 117 void setErrorCheckingEnabled(bool enable = true){errorCheckEnabled = enable;} 118 public final bool isRowMajor(){return true;} 119 120 Shader createShader() 121 { 122 version(HipGL3) 123 return new Shader(new Hip_GL3_ShaderImpl()); 124 else 125 return new Shader(new Hip_GL_ShaderImpl()); 126 } 127 ShaderVar* createShaderVar(ShaderTypes shaderType, UniformType uniformType, string varName, size_t length) 128 { 129 switch(uniformType) with(UniformType) 130 { 131 case texture_array: 132 { 133 return ShaderVar.createBlackboxed(shaderType, varName, uniformType, GLuint.sizeof*length, GLuint.sizeof); 134 } 135 default: return null; 136 } 137 } 138 version(dll)public bool initExternal(){return init(null);} 139 public bool init(HipWindow window) 140 { 141 this.window = window; 142 if(window !is null) 143 window.startOpenGLContext(); 144 version(Have_bindbc_opengl) 145 { 146 GLSupport ver = loadOpenGL(); 147 if(ver == GLSupport.noLibrary) 148 { 149 ErrorHandler.showErrorMessage("Loading OpenGL", "No OpenGL could be found"); 150 return false; 151 } 152 else if(ver == GLSupport.badLibrary) 153 { 154 ErrorHandler.showErrorMessage("Loading OpenGL", "OpenGL version is different than expected"); 155 } 156 } 157 rawlog("GL Renderer: ", glGetString(GL_RENDERER)); 158 rawlog("GL Version: ", glGetString(GL_VERSION)); 159 rawlog("GLSL Version: ", glGetString(GL_SHADING_LANGUAGE_VERSION)); 160 161 // setColor(); 162 HipRenderer.rendererType = HipRendererType.GL3; 163 return true; 164 } 165 166 void setShader(Shader s) 167 { 168 currentShader = s; 169 } 170 public int queryMaxSupportedPixelShaderTextures() 171 { 172 version(PSVita) 173 { 174 return 1; 175 } 176 else 177 { 178 int maxTex; 179 glCall(() => glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTex)); 180 return maxTex; 181 } 182 } 183 184 public void setColor(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 185 { 186 glCall(() => glClearColor(cast(float)r/255, cast(float)g/255, cast(float)b/255, cast(float)a/255)); 187 } 188 189 public IHipFrameBuffer createFrameBuffer(int width, int height) 190 { 191 return new Hip_GL3_FrameBuffer(width, height); 192 } 193 194 public IHipVertexArrayImpl createVertexArray() 195 { 196 version(HipGLUseVertexArray) 197 return new Hip_GL3_VertexArrayObject(); 198 else 199 return new Hip_GL_VertexArrayObject(); 200 } 201 public IHipTexture createTexture() 202 { 203 return new Hip_GL3_Texture(); 204 } 205 public IHipVertexBufferImpl createVertexBuffer(size_t size, HipBufferUsage usage) 206 { 207 return new Hip_GL3_VertexBufferObject(size, usage); 208 } 209 public IHipIndexBufferImpl createIndexBuffer(index_t count, HipBufferUsage usage) 210 { 211 return new Hip_GL3_IndexBufferObject(count, usage); 212 } 213 214 215 public void setViewport(Viewport v) 216 { 217 glCall(() => glViewport(cast(int)v.x, cast(int)v.y, cast(GLsizei)v.width, cast(GLsizei)v.height)); 218 } 219 public bool setWindowMode(HipWindowMode mode) 220 { 221 final switch(mode) with(HipWindowMode) 222 { 223 case BORDERLESS_FULLSCREEN: 224 break; 225 case FULLSCREEN: 226 break; 227 case WINDOWED: 228 229 break; 230 } 231 return false; 232 } 233 public bool hasErrorOccurred(out string err, string file = __FILE__, size_t line =__LINE__) 234 { 235 GLenum errorCode = glGetError(); 236 static enum GL_STACK_OVERFLOW = 0x0503; 237 static enum GL_STACK_UNDERFLOW = 0x0504; 238 switch(errorCode) 239 { 240 //Don't execute to!string(line) for avoiding useless GC trigger. 241 case GL_NO_ERROR: 242 err = `GL_NO_ERROR: No error has been recorded. The value of this symbolic constant is guaranteed to be 0.`; 243 break; 244 case GL_INVALID_ENUM: 245 err = `GL_INVALID_ENUM at `~file~":"~to!string(line)~`: 246 An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.`; 247 break; 248 case GL_INVALID_VALUE: 249 err = `GL_INVALID_VALUE at `~file~":"~to!string(line)~`: 250 A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.`; 251 break; 252 case GL_INVALID_OPERATION: 253 err = `GL_INVALID_OPERATION at `~file~":"~to!string(line)~`: 254 The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.`; 255 break; 256 case GL_INVALID_FRAMEBUFFER_OPERATION: 257 err = `GL_INVALID_FRAMEBUFFER_OPERATION at `~file~":"~to!string(line)~`: 258 The framebuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag.`; 259 break; 260 case GL_OUT_OF_MEMORY: 261 err = `GL_OUT_OF_MEMORY at `~file~":"~to!string(line)~`: 262 There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.`; 263 break; 264 case GL_STACK_UNDERFLOW: 265 err = `GL_STACK_UNDERFLOW at `~file~":"~to!string(line)~`: 266 An attempt has been made to perform an operation that would cause an internal stack to underflow.`; 267 break; 268 case GL_STACK_OVERFLOW: 269 err = `GL_STACK_OVERFLOW at `~file~":"~to!string(line)~`: 270 An attempt has been made to perform an operation that would cause an internal stack to overflow.`; 271 break; 272 default: 273 err = "Unknown error code"; 274 } 275 return errorCode != GL_NO_ERROR; 276 } 277 278 /** 279 * This function is used to control the internal state for creating vertex buffers 280 */ 281 public void begin(){} 282 283 /** 284 */ 285 public void end() 286 { 287 static if(UseGLES) 288 { 289 version(PSVita) 290 { 291 glCall(() => glFlush()); 292 glCall(() => glFinish()); 293 } 294 } 295 else 296 { 297 window.rendererPresent(); 298 // glCall(() => glFlush()); 299 // glCall(() => glFinish()); 300 } 301 } 302 303 public void clear() 304 { 305 uint clearMask = GL_COLOR_BUFFER_BIT; 306 if(isGLDepthEnabled) clearMask|= GL_DEPTH_BUFFER_BIT; 307 if(isGLStencilEnabled) clearMask|= GL_STENCIL_BUFFER_BIT; 308 309 glCall(() => glClear(clearMask)); 310 } 311 312 public void clear(ubyte r = 255, ubyte g = 255, ubyte b = 255, ubyte a = 255) 313 { 314 setColor(r,g,b,a); 315 clear(); 316 } 317 318 protected GLenum getGLRendererMode(HipRendererMode mode) 319 { 320 final switch(mode) with(HipRendererMode) 321 { 322 case POINT: return GL_POINTS; 323 case LINE: return GL_LINES; 324 case LINE_STRIP: return GL_LINE_STRIP; 325 case TRIANGLES: return GL_TRIANGLES; 326 case TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; 327 } 328 } 329 public void setRendererMode(HipRendererMode mode) 330 { 331 this.mode = getGLRendererMode(mode); 332 } 333 /** 334 * Offset is per byte based 335 */ 336 public void drawVertices(index_t count, uint offset) 337 { 338 glCall(() => glDrawArrays(this.mode, offset, count)); 339 } 340 /** 341 * Offset will always be based per index_t. 342 */ 343 public void drawIndexed(index_t indicesCount, uint offset = 0) 344 { 345 static if(is(index_t == uint)) 346 glCall(() => glDrawElements(this.mode, indicesCount, GL_UNSIGNED_INT, cast(void*)(offset*index_t.sizeof))); 347 else 348 glCall(() => glDrawElements(this.mode, indicesCount, GL_UNSIGNED_SHORT, cast(void*)(offset*index_t.sizeof))); 349 } 350 351 bool isBlendingEnabled() const {return isGLBlendEnabled;} 352 353 public void dispose() 354 { 355 if(window !is null) 356 { 357 window.destroyOpenGLContext(); 358 } 359 } 360 361 public void setDepthTestingFunction(HipDepthTestingFunction) 362 { 363 } 364 public void setDepthTestingEnabled(bool bEnable) 365 { 366 isGLDepthEnabled = bEnable; 367 if(bEnable) glCall(() => glEnable(GL_DEPTH_TEST)); 368 else glCall(() => glDisable(GL_DEPTH_TEST)); 369 } 370 371 public void setStencilTestingEnabled(bool bEnable) 372 { 373 // isGLStencilEnabled = bEnable; 374 isGLStencilEnabled = true; 375 GLboolean r = bEnable ? GL_FALSE : GL_TRUE; 376 // glCall(() => glColorMask(r, r, r, r)); 377 // glCall(() => glDepthMask(r)); 378 if(bEnable) glCall(() => glEnable(GL_STENCIL_TEST)); 379 else glCall(() => glDisable(GL_STENCIL_TEST)); 380 glClearStencil(0); 381 } 382 383 public void setColorMask(ubyte r, ubyte g, ubyte b, ubyte a) 384 { 385 glCall(() => glColorMask(r>0, g>0, b>0, a>0)); 386 } 387 388 public void setStencilTestingMask(uint mask) 389 { 390 if(mask > 0xFF) mask = 0xFF; 391 glCall(() => glStencilMask(mask)); 392 } 393 394 public void setStencilTestingFunction(HipStencilTestingFunction passFunc, uint reference, uint mask) 395 { 396 glCall(() => glStencilFunc(passFunc.fromHipStencilFunc, reference, mask)); 397 } 398 399 public void setStencilOperation(HipStencilOperation stencilFail, HipStencilOperation depthFail, HipStencilOperation stencilAndDephPass) 400 { 401 glCall(() => glStencilOp(stencilFail.fromHipStencilOperation, depthFail.fromHipStencilOperation, stencilAndDephPass.fromHipStencilOperation)); 402 } 403 404 }